//
// Created by song on 16-11-18.
//


#ifndef C0COMPILER_ERROR_H
#define C0COMPILER_ERROR_H

#include <map>
#include <vector>
#include <sstream>
#include <iostream>
#include <stdexcept>
#include "../Config.h"
#include "../front/Token.h"


class CommonError: public runtime_error{
public:
    int no;
    CommonError(int no, const string &__arg) : runtime_error(__arg), no(no) {}
};


class Error{
public:
    enum ErrorNo{// do not use static here.
        Can_Not_Open_File=1,
        Source_File_Too_Large=2,
        Should_Not_Happen=3,
        Too_Many_Errors = 4,

        // parse/syntax error.
        Expect_LeftR=     51,
        Expect_RightR=    52,
        Expect_LeftS=     53,
        Expect_RightS=    54,
        Expect_LeftB=     55,
        Expect_RightB=    56,
        Expect_Semicolon= 57,
        Expect_Colon=     58,
        Expect_Identifier=59,
        Expect_Number=    60,
        Expect_CharLiteral=61,
        Expect_Case   =   62,
        Expect_Assign=    63,
        Expect_Char_Int=  64,
        Expect_Void_Main= 65,
        Expect_Stmt =     66,
        Expect_Factor_Expr= 67,
        Invalid_Stmt_Expect_Assign_FunCall=68,

        // syntax error found by lexer.
        Invalid_String_Content = 80,
        Invalid_String_End =     81,
        Invalid_Char_Const_End = 82,
        Invalid_Char_Content =   83,
        Expect_Not_Equal =       84,

        // semantic error.
                Key_Not_In_AST = 101,
        Array_Size_Must_Larger_Than_Zero = 102,
        Exceeded_Max_Argument_Size = 103,
        Identifier_Declar_Not_Found = 104,
        Function_Declar_Not_Found  = 105,
        Array_Declar_Not_Found     = 106,
        Duplicate_Var_Name         = 107,
        Duplicate_Function_Name    = 108,
        Function_Invoke_Type_Mismatch=109,
        Function_Invoke_Arg_Too_Much= 110,
        Function_Invoke_Arg_Too_Few = 111,
        Call_Void_Function_In_Expr = 112,
        Array_Can_Not_Be_Argument =  113,
        Assignment_Type_Mismatch =   114,
        Const_Can_Not_Be_Assigned =  115,
        Array_Name_Can_Not_Be_Assigned=116,
        Function_Return_Type_Mismatch=117,
        Function_Need_Return        = 118,
        Array_Name_Can_Not_In_Expr  = 119,
        Number_Can_Not_Convert_To_Char_Const = 120,

    };



    static void warn(const char* msg){
        std::cout<<"[WARN]  "<< msg << endl;
    }

private:
    static map<ErrorNo,string> errorMsg;
    static map<ErrorNo,string> init(){
        map<ErrorNo,string> errorMsg;
        errorMsg[Can_Not_Open_File]= "can not open file";
        errorMsg[Source_File_Too_Large] = "source file too large. must less than MAX_SOURCE_FILE_SIZE";
        errorMsg[Expect_LeftR]=      "expect (";
        errorMsg[Expect_RightR]=     "expect )";
        errorMsg[Expect_LeftS]=      "expect [";
        errorMsg[Expect_RightS]=     "expect ]";
        errorMsg[Expect_LeftB]=      "expect {";
        errorMsg[Expect_RightB]=     "expect }";
        errorMsg[Expect_Semicolon]=  "expect ;";
        errorMsg[Expect_Colon]=      "expect :";
        errorMsg[Expect_Identifier]= "expect identifier";
        errorMsg[Expect_Number]=     "expect number";
        errorMsg[Expect_CharLiteral]="expect char literal";
        errorMsg[Expect_Stmt] = "expect statement";
        errorMsg[Expect_Case]=    "expect case";
        errorMsg[Expect_Assign]=     "expect =";
        errorMsg[Expect_Char_Int]=   "expect char or int";
        errorMsg[Expect_Factor_Expr] = "Expect a factor expression here";
        errorMsg[Expect_Void_Main]=  "function void main not found";
        errorMsg[Invalid_Stmt_Expect_Assign_FunCall]="invalid statement, expect assignment or function call";
        errorMsg[Key_Not_In_AST] = "key not found in ast node";
        errorMsg[Expect_Not_Equal] = "expect != but found ! only";
        errorMsg[Identifier_Declar_Not_Found] = "identifier declaration not found";
        errorMsg[Function_Declar_Not_Found] = "function declaration not found";
        errorMsg[Duplicate_Var_Name] = "variable or const already declared in the same scope.";
        errorMsg[Duplicate_Function_Name] = "function name already declared";
        errorMsg[Call_Void_Function_In_Expr] = "need a non-void function";
        errorMsg[Function_Invoke_Type_Mismatch] = "function call argument type mismatch";
        errorMsg[Function_Invoke_Arg_Too_Few] = "need more arguments";
        errorMsg[Function_Invoke_Arg_Too_Much] = "too much arguments";
        errorMsg[Array_Can_Not_Be_Argument] = "arrays are not allow passed as argument";
        errorMsg[Array_Name_Can_Not_In_Expr] = "array name can only be used in array subscript expression";
        errorMsg[Assignment_Type_Mismatch] = "assignment type mismatch";
        errorMsg[Array_Declar_Not_Found] = "declaration of array not found";
        errorMsg[Const_Can_Not_Be_Assigned] = "const var can not be assigned";
        errorMsg[Array_Name_Can_Not_Be_Assigned] = "array name can not be assigned";
        errorMsg[Function_Return_Type_Mismatch] = "return type do not match function return type";
        errorMsg[Function_Need_Return] = "function should has at least one return statement";
        errorMsg[Number_Can_Not_Convert_To_Char_Const] = "number can not convert to a valid char const";
        errorMsg[Should_Not_Happen] = "Oops!! You just hit a BUG in the compiler!!";
        errorMsg[Too_Many_Errors] =  "Too Many Errors!";
        errorMsg[Invalid_String_Content] = "Invalid string content";
        errorMsg[Invalid_String_End] = "not a valid string end";
        errorMsg[Invalid_Char_Content] = "invalid char content";
        errorMsg[Array_Size_Must_Larger_Than_Zero] = "array size must larger than 0";
        errorMsg[Exceeded_Max_Argument_Size] = "function can only has less than 32 arguments";
        return errorMsg;
    }

    static string buildErrorMsg(ErrorNo no){
        stringstream r;
        if(errorMsg.count(no)>0){
            r<<"Error: "<<errorMsg[no]<<endl<<"Details: ";
        }else{
            r<<"Error("<<no<<") occurred. But no error description."<<endl<<"Details: ";
        }
        string detail = nextErrorDetail.str();
        if(!detail.empty()){
            r<<detail;
            nextErrorDetail.str(std::string());
            nextErrorDetail.clear();
        }
        return r.str();
    }


public:
    static vector<CommonError*> errorList;
    static stringstream nextErrorDetail;

    static void printFirstError(){
        if(!errorList.empty()){
            cout << "####### First of total "<< errorList.size() <<" errors #######" << endl;
            CommonError* err = errorList[0];
            cout << err->what() << endl;
        }
    }

    static void printAllErrors(){
        cout << "####### ALL ERRORS #######" << endl;
        for(int i=0; i<errorList.size(); i++){
            CommonError* err = errorList[i];
            cout << err->what() << endl;
        }
    }



    static void syntax(int sourceIndex, ErrorNo value){
        SyntaxError* err = new SyntaxError(sourceIndex, value, buildErrorMsg(value));
        CommonError* cErr = err;
        errorList.push_back(cErr);
        throw err;
    }

    static void internal(ErrorNo value){
        InternalError* err = new InternalError(value, buildErrorMsg(value));
        CommonError* cErr = err;
        errorList.push_back(cErr);
        throw err;
    }

    static void compilerError(ErrorNo value){
        InternalError* err = new InternalError(value, buildErrorMsg(value));
        CommonError* cErr = err;
        errorList.push_back(cErr);
        throw err;
    }

    static void semantic(ErrorNo value){
        SemanticError* err = new SemanticError(value, buildErrorMsg(value));
        CommonError* cErr = err;
        errorList.push_back(cErr);
        throw err;
    }

    class SyntaxError: public CommonError{
    public:
        int sourceIndex;
        ErrorNo no;
        SyntaxError(int sourceIndex, ErrorNo n, const string &arg):
                CommonError(n, arg),
                sourceIndex(sourceIndex),
                no(n)
        {}
        bool equals(SyntaxError* err){
            return (no==err->no && sourceIndex==err->sourceIndex);
        }
    };

    class SemanticError: public CommonError{
    public:
        SemanticError(ErrorNo no, const string &arg) : CommonError(no, arg) {}
    };

    class InternalError: public CommonError{
    public:
        InternalError(ErrorNo no, const string &arg) : CommonError(no, arg) {}
    };
};


#endif //C0COMPILER_ERROR_H
